import p, { flat, suffix } from "./p";

export class ObjectCode {
  entries: { key: string; value: string[] | ObjectCode }[] = [];

  findArrayCode(key: string) {
    return this.entries.find(
      (entry): entry is { key: string; value: string[] } =>
        entry.key === key && Array.isArray(entry.value)
    );
  }

  findObjectCode(key: string) {
    return this.entries.find(
      (entry): entry is { key: string; value: ObjectCode } =>
        entry.key === key && !Array.isArray(entry.value)
    );
  }

  private addArrayValue(key: string, value: string[]) {
    const entry = this.findArrayCode(key);
    if (entry) {
      entry.value.push(...value);
    } else {
      this.entries.push({ key, value });
    }
  }

  private addNestedValue(key: string, rest: string[], value: string[]) {
    const entry = this.findObjectCode(key);
    if (entry) {
      entry.value.add(rest, value);
    } else {
      this.entries.push({ key, value: new ObjectCode().add(rest, value) });
    }
  }

  /**
   * Use array to represent a path, and use string[] to represent a value.
   * @param key The key of the object, the path array.
   * @param value The value of the object, the multi-line code.
   */
  add(path: string[], value: string[]) {
    if (!path.length) return this;
    if (path.length === 1) {
      this.addArrayValue(path[0], value);
    } else {
      this.addNestedValue(path[0], path.slice(1), value);
    }
    return this;
  }

  private getRecords() {
    const record: Record<string, string[]> = {};
    this.entries.forEach(({ key, value }) => {
      if (key in record) {
        if (Array.isArray(value)) {
          record[key].push(...suffix(value, ","));
        } else {
          record[key].push(...suffix(value.printContent(), ","));
        }
      } else {
        if (Array.isArray(value)) {
          record[key] = suffix(value, ",");
        } else {
          record[key] = suffix(value.printContent(), ",");
        }
      }
    });
    return record;
  }

  private printContent() {
    const record = this.getRecords();
    return flat(Object.entries(record).map(([key, value]) => {
      return [`${key}: {`, ...p.suffix(value, ","), "},"];
    }));
  }

  print(): string[] {
    return [
      "{",
      ...p(this.printContent()),
      "}",
    ];
  }
}
